package vip.liux.backend.application.serviceImpl.product;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import vip.liux.application.dots.ListResultDto;
import vip.liux.application.dots.PagedResultDto;
import vip.liux.backend.application.service.product.*;
import vip.liux.backend.infrastructure.code.BizCodeGenerator;
import vip.liux.contracts.models.product.Product;
import vip.liux.contracts.models.product.ProductDetail;
import vip.liux.contracts.repositories.product.ProductDetailRepository;
import vip.liux.contracts.repositories.product.ProductRepository;
import vip.liux.contracts.shared.enums.Status;
import vip.liux.contracts.shared.enums.product.ImageType;
import vip.liux.contracts.shared.enums.product.ShelfState;
import vip.liux.contracts.shared.value.Image;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static vip.liux.application.PageKit.toPage;

/**
 * 商品服务类
 * 提供商品的保存、查询等相关操作
 */
@Service
public class ProductServiceImpl implements ProductService {

    private static final Logger log = LoggerFactory.getLogger(ProductServiceImpl.class);
    private final BizCodeGenerator bizCodeGenerator;
    private final CategoryService categoryService;
    private final BrandService brandService;
    private final ProductRepository productRepository;
    private final ProductDetailRepository productDetailRepository;
    private final TransactionTemplate transactionTemplate;

    @Autowired(required = false)
    private List<CheckOccupied> checkOccupied = new ArrayList<>();

    public ProductServiceImpl(BizCodeGenerator bizCodeGenerator, CategoryService categoryService, BrandService brandService, ProductRepository productRepository, ProductDetailRepository productDetailRepository, TransactionTemplate transactionTemplate) {
        this.bizCodeGenerator = bizCodeGenerator;
        this.categoryService = categoryService;
        this.brandService = brandService;
        this.productRepository = productRepository;
        this.productDetailRepository = productDetailRepository;
        this.transactionTemplate = transactionTemplate;
    }

    /**
     * 保存商品信息
     * 包括商品的基本信息和 SKU 详细信息
     *
     * @param input 商品保存请求数据
     * @return 保存后的商品实体
     */
    public ProductSpuDto save(CreateProductDto input) {
        log.info("开始保存商品信息: {}", input.getProductName());
        Product product = new Product();
        fillCategoryInfo(product, categoryService.getCategoriesOfAllParent(input.getCatNo()));
        product.setBrandNo(input.getBrandNo());
        product.setProductName(input.getProductName());
        product.setRemark(input.getRemark());
        product.setImages(buildImages(input.getMainImages(), input.getDetailImages()));
        product.setSpecs(input.getSpecs());
        product.setDesc(input.getDesc());
        product.setDeductStockType(input.getDeductStockType());
        product.setStatus(Status.ENABLED);
        product.setSpu(bizCodeGenerator.generateSpu(product.getCatParentNo(), product.getCatNo()));

        List<ProductDetail> details = input.getDetails().stream().map(d -> {
            ProductDetail detail = AutoMapperProfile.INSTANCE.toEntity(d);
            detail.setSpu(product.getSpu());
            detail.setSku(bizCodeGenerator.generateSku(product.getCatParentNo(), product.getCatNo()));
            detail.setShelfState(ShelfState.ON_SHELF);
            return detail;
        }).toList();

        product.setStockTotal(details.stream().map(ProductDetail::getStockTotal).reduce(0, Integer::sum));

        Product savedProduct = transactionTemplate.execute((status) -> {
            productRepository.save(product);
            productDetailRepository.saveAll(details);
            return product;
        });
        log.info("商品保存成功: {}", savedProduct.getSpu());
        return AutoMapperProfile.INSTANCE.toSpuDto(savedProduct, details);
    }

    /**
     * 分页查询商品列表
     *
     * @param input 分页请求参数
     * @return 分页结果
     */
    public PagedResultDto<ProductSpuDto> pageList(QueryProductDto input) {
        Page<Product> page = productRepository.findAll(input.getProductName(), input.getSpu(), input.getCatNo(), input.getBrandNo(), input.getStatus(), toPage(input));
        List<ProductDetail> details = productDetailRepository.findBySpuIn(page.getContent().stream().map(Product::getSpu).toList());
        List<ProductSpuDto> spuDto = toSpuDto(page.getContent(), details);
        return new PagedResultDto<>(spuDto, page.getTotalElements());
    }

    /**
     * 根据 SPU 查询商品信息
     *
     * @param spu 商品 SPU 编码
     * @return 商品 SPU 数据传输对象
     */
    public ProductSpuDto detail(String spu) {
        Product product = getProductOrThrow(spu);
        List<ProductDetail> details = productDetailRepository.findBySpu(spu);
        return toSpuDto(List.of(product), details).stream().findFirst().orElseThrow(() -> new IllegalArgumentException("spu 编码:" + spu + " 不存在"));
    }

    /**
     * 根据 SKU 查询商品信息
     *
     * @param sku 商品 SKU 编码
     * @return 商品 SKU 数据传输对象
     */
    public ProductSkuDto detailBySku(String sku) {
        ProductDetail productDetail = productDetailRepository.findBySku(sku)
                .orElseThrow(() -> new IllegalArgumentException("sku 编码:" + sku + " 不存在"));
        Product product = getProductOrThrow(productDetail.getSpu());
        ProductSkuDto skuDto = AutoMapperProfile.INSTANCE.toSkuDto(product, productDetail);
        fillBrandAndCategoryInfo(skuDto, product.getBrandNo(), List.of(skuDto.getCatRootNo(), skuDto.getCatParentNo(), skuDto.getCatNo()));
        return skuDto;
    }

    @Override
    public ProductSpuDto update(String spu, CreateProductDto dto) {
        log.info("开始更新商品信息: {}", spu);
        return transactionTemplate.execute(status -> {
            Product product = getProductOrThrow(spu);
            fillCategoryInfo(product, categoryService.getCategoriesOfAllParent(dto.getCatNo()));
            product.setBrandNo(dto.getBrandNo());
            product.setProductName(dto.getProductName());
            product.setRemark(dto.getRemark());
            product.setImages(buildImages(dto.getMainImages(), dto.getDetailImages()));
            product.setSpecs(dto.getSpecs());
            product.setDesc(dto.getDesc());
            product.setDeductStockType(dto.getDeductStockType());

            List<ProductDetail> oldDetails = productDetailRepository.findBySpu(spu);
            Map<String, ProductDetail> oldDetailMap = oldDetails.stream().collect(Collectors.toMap(ProductDetail::getSku, d -> d));

            List<ProductDetail> newDetails = dto.getDetails().stream().map(d -> {
                ProductDetail detail = AutoMapperProfile.INSTANCE.toEntity(d);
                detail.setSpu(product.getSpu());
                String sku = StringUtils.isBlank(d.getSku()) ? bizCodeGenerator.generateSku(product.getCatParentNo(), product.getCatNo()) : d.getSku();
                detail.setSku(sku);
                detail.setShelfState(ShelfState.ON_SHELF);
                return detail;
            }).toList();

            product.setStockTotal(newDetails.stream().map(ProductDetail::getStockTotal).reduce(0, Integer::sum));
            productRepository.save(product);

            Map<String, ProductDetail> newDetailMap = newDetails.stream().collect(Collectors.toMap(ProductDetail::getSku, d -> d));

            List<ProductDetail> toDelete = oldDetails.stream().filter(d -> !newDetailMap.containsKey(d.getSku())).toList();
            if (!toDelete.isEmpty()) {
                productDetailRepository.deleteAll(toDelete);
            }

            List<ProductDetail> toAdd = newDetails.stream().filter(d -> !oldDetailMap.containsKey(d.getSku())).toList();
            if (!toAdd.isEmpty()) {
                productDetailRepository.saveAll(toAdd);
            }

            List<ProductDetail> toUpdate = newDetails.stream().filter(d -> oldDetailMap.containsKey(d.getSku())).peek(d -> d.setId(oldDetailMap.get(d.getSku()).getId())).toList();
            if (!toUpdate.isEmpty()) {
                productDetailRepository.saveAll(toUpdate);
            }

            return AutoMapperProfile.INSTANCE.toSpuDto(product, newDetails);
        });
    }

    @Transactional
    @Override
    public void delete(List<String> spuList) {
        log.info("开始删除商品: {}", spuList);
        spuList.forEach(spu -> {
            if (checkOccupied.stream().noneMatch(c -> c.isOccupied(spu))) {
                log.info("Deleting product with SPU: {}", spu);
                productRepository.deleteBySpu(spu);
                productDetailRepository.deleteBySpu(spu);
            }
        });
    }

    @Override
    public void status(List<String> spuList, Status status) {
        log.info("开始更新商品状态: {}, 目标状态: {}", spuList, status);
        if (Status.DISABLED.equals(status)) {
            List<String> before = spuList;
            spuList = spuList.stream().filter(spu -> checkOccupied.stream().noneMatch(c -> c.isOccupied(spu))).toList();
            log.info("禁用商品前占用校验，原始SPU: {}，可操作SPU: {}", before, spuList);
        }
        if (spuList.isEmpty()) {
            log.info("无可操作SPU，状态变更跳过，目标状态: {}", status);
            return;
        }
        log.info("批量更新商品状态，SPU: {}，目标状态: {}", spuList, status);
        productRepository.updateStatusBySpuIn(spuList, status);
    }

    @Override
    public PagedResultDto<ProductSkuDto> skuList(QueryProductDto input) {
        Page<Product> page = productRepository.findAll(input.getProductName(), input.getSpu(), input.getCatNo(), input.getBrandNo(), input.getStatus(), toPage(input));
        List<ProductDetail> details = productDetailRepository.findBySpuIn(page.getContent().stream().map(Product::getSpu).toList());
        List<ProductSkuDto> spuDto = toSkuDto(page.getContent(), details);
        return new PagedResultDto<>(spuDto, page.getTotalElements());
    }

    @Override
    public ListResultDto<ProductSkuDto> listBySku(List<String> skuList) {
        List<ProductDetail> details = productDetailRepository.findBySkuIn(skuList);
        List<Product> products = productRepository.findBySpuIn(details.stream().map(ProductDetail::getSpu).toList());
        List<ProductSkuDto> skuDtos = toSkuDto(products, details);
        return new ListResultDto<>(skuDtos);
    }

    private List<ProductSkuDto> toSkuDto(List<Product> products, List<ProductDetail> details) {
        return details.stream().map(detail -> {
            Product product = products.stream().filter(p -> StringUtils.equals(p.getSpu(), detail.getSpu())).findFirst()
                    .orElse(new Product());
            ProductSkuDto skuDto = AutoMapperProfile.INSTANCE.toSkuDto(product, detail);
            fillBrandAndCategoryInfo(skuDto, product.getBrandNo(), List.of(skuDto.getCatRootNo(), skuDto.getCatParentNo(), skuDto.getCatNo()));
            return skuDto;
        }).toList();
    }

    private List<ProductSpuDto> toSpuDto(List<Product> products, List<ProductDetail> details) {
        return products.stream().map(product -> {
            List<ProductDetail> productDetails = details.stream().filter(detail -> StringUtils.equals(product.getSpu(), detail.getSpu())).toList();
            ProductSpuDto spuDto = AutoMapperProfile.INSTANCE.toSpuDto(product, productDetails);
            fillBrandAndCategoryInfo(spuDto, product.getBrandNo(), List.of(spuDto.getCatRootNo(), spuDto.getCatParentNo(), spuDto.getCatNo()));
            return spuDto;
        }).toList();
    }

    private void fillBrandAndCategoryInfo(ProductExtInfo spuDto, String brandNo, List<String> catNos) {
        Optional.ofNullable(brandService.findByBrandNo(brandNo)).ifPresent(brand -> spuDto.setBrandName(brand.getBrandName()));
        Map<String, String> catMap = categoryService.findByCatNos(catNos).stream().collect(Collectors.toMap(CategoryDto::getCatNo, CreateCategoryDto::getCatName, (c1, c2) -> c1));
        spuDto.setCatRootName(catMap.get(spuDto.getCatRootNo()));
        spuDto.setCatParentName(catMap.get(spuDto.getCatParentNo()));
        spuDto.setCatName(catMap.get(spuDto.getCatNo()));
    }

    private void fillCategoryInfo(Product product, CategoryDto category) {
        product.setCatNo(category.getCatNo());
        CategoryDto parent = category.getParent();
        if (parent != null) {
            product.setCatParentNo(parent.getCatNo());
            CategoryDto root = parent.getParent();
            if (root != null) {
                product.setCatRootNo(root.getCatNo());
            }
        }
    }

    private List<Image> buildImages(List<String> mainImages, List<String> detailImages) {
        return Stream.concat(
                mainImages.stream().map(img -> new Image(ImageType.MAIN.getValue(), img)),
                detailImages.stream().map(img -> new Image(ImageType.DETAIL.getValue(), img))
        ).toList();
    }

    private Product getProductOrThrow(String spu) {
        return productRepository.findBySpu(spu).orElseThrow(() -> new IllegalArgumentException("商品SPU不存在: " + spu));
    }
}